package org.yzhzc.system.service.exam.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.yzhzc.common.core.constants.Constants;
import org.yzhzc.common.core.enums.ResultCode;
import org.yzhzc.common.security.exception.ServiceException;
import org.yzhzc.system.domain.exam.dto.*;
import org.yzhzc.system.domain.exam.entity.Exam;
import org.yzhzc.system.domain.exam.entity.ExamQuestion;
import org.yzhzc.system.domain.exam.vo.*;
import org.yzhzc.system.domain.question.entity.Question;
import org.yzhzc.system.domain.question.vo.QuestionVO;
import org.yzhzc.system.manager.ExamCacheManager;
import org.yzhzc.system.mapper.exam.ExamMapper;
import org.yzhzc.system.mapper.exam.ExamQuestionMapper;
import org.yzhzc.system.mapper.question.QuestionMapper;
import org.yzhzc.system.service.exam.IExamService;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Slf4j
@Service
public class ExamServiceImpl extends ServiceImpl<ExamQuestionMapper, ExamQuestion> implements IExamService {

    @Autowired
    private ExamMapper examMapper;

    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private ExamQuestionMapper examQuestionMapper;

    @Autowired
    private ExamCacheManager examCacheManager;

    /**
     * 获取竞赛列表，支持分页
     *
     * @param examQueryDTO 查询条件
     * @return 竞赛列表
     */
    @Override
    public List<ExamVO> list(ExamQueryDTO examQueryDTO) {
        // 使用 PageHelper 分页
        PageHelper.startPage(examQueryDTO.getPageNum(), examQueryDTO.getPageSize());
        return examMapper.selectExamList(examQueryDTO);
    }

    /**
     * 添加新的竞赛
     *
     * @param examAddDTO 竞赛添加信息
     * @return 竞赛ID
     */
    @Override
    public String add(ExamAddDTO examAddDTO) {
        // 校验竞赛保存参数是否合法
        checkExamSaveParams(examAddDTO, null);
        // 将 DTO 转换为实体对象
        Exam exam = new Exam();
        BeanUtil.copyProperties(examAddDTO, exam);
        exam.setCreateBy(1L);  // 设置创建者为假定的用户ID
        examMapper.insert(exam);
        return exam.getExamId().toString();
    }

    /**
     * 为竞赛添加问题
     *
     * @param examQuestAddDTO 竞赛问题添加信息
     * @return 是否成功
     */
    @Override
    public boolean questionAdd(ExamQuestAddDTO examQuestAddDTO) {
        // 获取竞赛信息
        Exam exam = getExam(examQuestAddDTO.getExamId());

        // 校验竞赛的日期和状态
        checkExam(exam);

        // 如果竞赛已经发布，不能再编辑
        if (Constants.TRUE.equals(exam.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }

        Set<Long> questionIdSet = examQuestAddDTO.getQuestionIdSet();
        if (CollectionUtil.isEmpty(questionIdSet)) {
            return true;
        }
        List<Question> questionList = questionMapper.selectByIds(questionIdSet);
        if (CollectionUtil.isEmpty(questionList) || questionList.size() < questionIdSet.size()) {
            throw new ServiceException(ResultCode.EXAM_QUESTION_NOT_EXISTS);
        }

        return saveExamQuestion(exam, questionIdSet);
    }

    /**
     * 删除竞赛中的问题
     *
     * @param examId 竞赛ID
     * @param questionId 问题ID
     * @return 删除的记录数
     */
    @Override
    public int questionDelete(Long examId, Long questionId) {
        // 获取竞赛信息
        Exam exam = getExam(examId);
        checkExam(exam);

        // 如果竞赛已发布，不能删除问题
        if (Constants.TRUE.equals(exam.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        return examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId)
                .eq(ExamQuestion::getQuestionId, questionId));
    }

    /**
     * 获取竞赛详情，包括竞赛的基本信息和所有问题信息
     *
     * @param examId 竞赛ID
     * @return 竞赛详情
     */
    @Override
    public ExamDetailVO detail(Long examId) {
        ExamDetailVO examDetailVO = new ExamDetailVO();
        Exam exam = getExam(examId);
        BeanUtil.copyProperties(exam, examDetailVO);

        // 获取竞赛问题列表
        List<ExamQuestion> examQuestionList = examQuestionMapper.selectList(new LambdaQueryWrapper<ExamQuestion>()
                .select(ExamQuestion::getQuestionId)
                .eq(ExamQuestion::getExamId, examId)
                .orderByAsc(ExamQuestion::getQuestionOrder));

        if (CollectionUtil.isEmpty(examQuestionList)) {
            // 返回只包含竞赛基本信息的详情
            return examDetailVO;
        }

        // 获取所有问题的信息
        List<Long> questionIdList = examQuestionList.stream().map(ExamQuestion::getQuestionId).toList();
        List<Question> questionList = questionMapper.selectList(new LambdaQueryWrapper<Question>()
                .select(Question::getQuestionId, Question::getTitle, Question::getDifficulty)
                .in(Question::getQuestionId, questionIdList));

        // 转换为 QuestionVO 并设置到竞赛详情中
        List<QuestionVO> questionVOList = BeanUtil.copyToList(questionList, QuestionVO.class);
        examDetailVO.setExamQuestionList(questionVOList);

        return examDetailVO;
    }

    /**
     * 编辑竞赛信息
     *
     * @param examEditDTO 竞赛编辑信息
     * @return 更新的记录数
     */
    @Override
    public int edit(ExamEditDTO examEditDTO) {
        // 获取竞赛信息
        Exam exam = getExam(examEditDTO.getExamId());

        // 校验竞赛是否已发布
        if (Constants.TRUE.equals(exam.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        checkExam(exam);

        // 校验编辑的参数
        checkExamSaveParams(examEditDTO, examEditDTO.getExamId());

        // 更新竞赛的标题和时间
        exam.setTitle(examEditDTO.getTitle());
        exam.setStartTime(examEditDTO.getStartTime());
        exam.setEndTime(examEditDTO.getEndTime());
        return examMapper.updateById(exam);
    }

    /**
     * 删除竞赛
     *
     * @param examId 竞赛ID
     * @return 删除的记录数
     */
    @Override
    public int delete(Long examId) {
        // 获取竞赛信息
        Exam exam = getExam(examId);

        // 校验竞赛是否已发布
        if (Constants.TRUE.equals(exam.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        checkExam(exam);

        // 删除竞赛的所有问题
        examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId));
        return examMapper.deleteById(exam);
    }

    /**
     * 发布竞赛
     *
     * @param examId 竞赛ID
     * @return 更新的记录数
     */
    @Override
    public int publish(Long examId) {
        // 获取竞赛信息
        Exam exam = getExam(examId);

        // 检查竞赛是否已结束
        if (exam.getEndTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_IS_FINISH);
        }

        // 检查竞赛是否有题目
        Long count = examQuestionMapper
                .selectCount(new LambdaQueryWrapper<ExamQuestion>().eq(ExamQuestion::getExamId, examId));
        if (count == null || count <= 0) {
            throw new ServiceException(ResultCode.EXAM_NOT_HAS_QUESTION);
        }

        // 设置竞赛为已发布状态
        exam.setStatus(Constants.TRUE);

        // 将竞赛数据存储到 Redis
        examCacheManager.addCache(exam);

        return examMapper.updateById(exam);
    }

    /**
     * 取消发布竞赛
     *
     * @param examId 竞赛ID
     * @return 更新的记录数
     */
    @Override
    public int cancelPublish(Long examId) {
        // 获取竞赛信息
        Exam exam = getExam(examId);
        checkExam(exam);

        // 检查竞赛是否已结束
        if (exam.getEndTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_IS_FINISH);
        }

        // 设置竞赛为未发布状态
        exam.setStatus(Constants.FALSE);

        // 从 Redis 删除竞赛缓存
        examCacheManager.deleteCache(examId);

        return examMapper.updateById(exam);
    }

    // 校验竞赛保存时的参数合法性
    private void checkExamSaveParams(ExamAddDTO examSaveDTO, Long examId) {
        // 1. 校验竞赛标题是否重复 2. 校验竞赛时间是否合法
        List<Exam> examList = examMapper
                .selectList(new LambdaQueryWrapper<Exam>()
                        .eq(Exam::getTitle, examSaveDTO.getTitle())
                        .ne(examId != null, Exam::getExamId, examId));
        if (CollectionUtil.isNotEmpty(examList)) {
            throw new ServiceException(ResultCode.FAILED_ALREADY_EXISTS);
        }
        if (examSaveDTO.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_BEFORE_CURRENT_TIME);  // 竞赛开始时间不能早于当前时间
        }
        if (examSaveDTO.getStartTime().isAfter(examSaveDTO.getEndTime())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_AFTER_END_TIME);  // 竞赛开始时间不能晚于结束时间
        }
    }

    // 校验竞赛是否已开始
    private void checkExam(Exam exam) {
        if (exam.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_STARTED);  // 竞赛已经开始
        }
    }

    // 保存竞赛与问题的关系
    private boolean saveExamQuestion(Exam exam, Set<Long> questionIdSet) {
        int num = 1;
        List<ExamQuestion> examQuestionList = new ArrayList<>();
        for (Long questionId : questionIdSet) {
            ExamQuestion examQuestion = new ExamQuestion();
            examQuestion.setExamId(exam.getExamId());
            examQuestion.setQuestionId(questionId);
            examQuestion.setQuestionOrder(num++);
            examQuestionList.add(examQuestion);

        }
        return saveBatch(examQuestionList);
    }

    // 根据竞赛ID获取竞赛信息
    private Exam getExam(Long examId) {
        Exam exam = examMapper.selectById(examId);
        if (exam == null) {
            throw new ServiceException(ResultCode.FAILED_NOT_EXISTS);  // 竞赛不存在
        }
        return exam;
    }
}
